package com.joysuch.wwyt.dynamicdata;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.dynamicdata.enums.ProcessorSelector;
import com.joysuch.wwyt.util.ReflectUtil;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.Map;

@Aspect
@Component
public class DynamicDataEngine {

    private static final Logger logger = LoggerFactory.getLogger(DynamicDataEngine.class);

    private final IDynamicProcessorFactory processorFactory;

    public DynamicDataEngine() {
        this.processorFactory = new DynamicDataProcessor();
    }

    /**
     * 读切点
     */
    @Pointcut("@annotation(dynamicReadData)")
    public void dynamicReadPoint(DynamicReadData dynamicReadData) {
    }

    /**
     * 写切点
     */
    @Pointcut("@annotation(dynamicWriteData)")
    public void dynamicWritePoint(DynamicWriteData dynamicWriteData) {
    }

    /**
     * 导入切点
     */
    @Pointcut("@annotation(dynamicImportData)")
    public void dynamicImportPoint(DynamicImportData dynamicImportData) {
    }

    /**
     * Dynamic read
     *
     * @param joinPoint
     * @param dynamicReadData
     * @return
     */
    @Around(value = "dynamicReadPoint(dynamicReadData)", argNames = "joinPoint,dynamicReadData")
    public Object aroundDynamicRead(ProceedingJoinPoint joinPoint, DynamicReadData dynamicReadData) {
        String selector = dynamicReadData.selector();
        int handlerIndex = dynamicReadData.handlerIndex();
        Object[] args = joinPoint.getArgs();
        String dataDumpHandler = args[handlerIndex].toString();
        try {
            Object proceedResult = joinPoint.proceed();
            return processorFactory.readProcess(proceedResult, dataDumpHandler, selector);
        } catch (Throwable throwable) {
            logger.error("AroundDynamicRead readData >> dynamic read data error", throwable);
            throw new RuntimeException(throwable.getMessage());
        }
    }

    /**
     * Dynamic import
     *
     * @param joinPoint
     * @param dynamicImportData
     * @return
     */
    @Around(value = "dynamicImportPoint(dynamicImportData)", argNames = "joinPoint,dynamicImportData")
    public Object aroundDynamicImport(ProceedingJoinPoint joinPoint, DynamicImportData dynamicImportData) {
        try {
            Object proceedResult = joinPoint.proceed();
            Object[] args = joinPoint.getArgs();
            int handlerIndex = dynamicImportData.handlerIndex();
            String dataDumpHandler = args[handlerIndex].toString();
            int fileIndex = dynamicImportData.fileIndex();
            MultipartFile file = (MultipartFile) args[fileIndex];
            String selector = dynamicImportData.selector();
            processorFactory.importProcess(file, dataDumpHandler, selector);
            return proceedResult;
        } catch (Throwable throwable) {
            logger.error("dynamicImportPoint importData >> dynamic import data error", throwable);
            throw new RuntimeException(throwable.getMessage());
        }
    }

    /**
     * Dynamic write
     *
     * @param joinPoint
     * @param dynamicWriteData
     * @return
     */
    @Around(value = "dynamicWritePoint(dynamicWriteData)", argNames = "joinPoint,dynamicWriteData")
    public Object aroundDynamicWrite(ProceedingJoinPoint joinPoint, DynamicWriteData dynamicWriteData) {
        Object[] args = joinPoint.getArgs();
        int argIndex = dynamicWriteData.argIndex();
        String selector = dynamicWriteData.selector();
        String paramName = dynamicWriteData.paramName();
        String bizType = dynamicWriteData.bizType();
        if (StringUtils.isBlank(bizType)) {
            throw new IllegalArgumentException("AroundDynamicWrite writeData >> bizType is null.");
        }
        return writeData(joinPoint, paramName, args[argIndex], bizType, selector);
    }

    private Object writeData(final ProceedingJoinPoint joinPoint, final String paramName, final Object arg, final String bizType, final String selector) {
        try {
            Object proceedResult = joinPoint.proceed();
            if (proceedResult instanceof ResultBean) {
                ResultBean resultBean = (ResultBean) proceedResult;
                if (ResultBean.SUCCESS_CODE != resultBean.getCode()) {
                    return proceedResult;
                }
            }
            Map<String, Object> keyAndValue = ReflectUtil.getKeyAndValue(arg);
            if (MapUtils.isNotEmpty(keyAndValue)) {
                Object valueObj = keyAndValue.get(paramName);
                if (valueObj != null) {
                    Object bizId = null;
                    if (ProcessorSelector.INSERT.getValue().equals(selector)) {
                        bizId = parseBizId(proceedResult);
                    }
                    processorFactory.writeProcess(valueObj, bizType, selector, bizId);
                } else {
                    logger.warn("dynamicWriteData paramName:{} not find", paramName);
                }
            }
            return proceedResult;
        } catch (Throwable throwable) {
            logger.error("aroundDynamicWrite writeData >> dynamic write data error", throwable);
            throw new RuntimeException(throwable.getMessage());
        }
    }

    private Object parseBizId(Object proceedResult) {
        if (proceedResult instanceof ResultBean) {
            JSONObject proceedObject = JSONUtil.parseObj(proceedResult);
            Object data = proceedObject.get(DynamicDataConstants.DATA);
            if (data instanceof Number) {
                return data;
            } else {
                JSONObject dataObject = JSONUtil.parseObj(data);
                return dataObject.get(DynamicDataConstants.ID);
            }
        } else {
            JSONObject dataObject = JSONUtil.parseObj(proceedResult);
            return dataObject.get(DynamicDataConstants.ID);
        }
    }

}

